package kr.pe.johnkim.checkmileage.mobile.rest.controller;

import kr.pe.johnkim.checkmileage.mobile.domain.CheckMileageCertification;
import kr.pe.johnkim.checkmileage.mobile.rest.service.CheckMileageCertificationService;
import kr.pe.johnkim.checkmileage.mobile.util.CheckMileageCommon;
import org.codehaus.jettison.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Controller;

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * 인증 관련 Controller 클래스 이다.
 *
 * @author : johnkim <cale9797@gmail.com / Bettersoft, Inc.>
 * @Date : 12. 8. 3
 */

@Controller
@Path("/checkMileageCertificationController")
public class CheckMileageCertificationController {

    // SLF4J Logging
	private Logger logger = LoggerFactory.getLogger(CheckMileageCertificationController.class);

    @Autowired
    private CheckMileageCertificationService checkMileageCertificationService = null;

    @Autowired
    private CheckMileageCommon checkMileageCommon = null;

    @Autowired
    private TaskExecutor taskExecutor;

    // 승인 요청 메소드 호출 여부.
    private boolean REQUEST_ADMISSION;

    // taskExecutor 실행 횟수 확인.
    private int REQUEST_INTERVAL = 0;

    /**
     * 해당 사용자가 인증 번호를 요청 한다.
     *
     * 1. 인증 번호 생성.
     * 2. 생성한 인증번호를 database 에 저장.
     * 3. 생성한 인증번호를 해당 사용자에게 SMS 로 전송.
     *
     * @param is
     * @return
     * @throws Exception
     */
    @POST
    @Path("/requestCertification")
    @Consumes({"application/json", "application/xml"})
    @Produces({"application/json", "application/xml"})
    public CheckMileageCertification requestCertification(InputStream is) throws Exception {
        logger.info("requestCertification");

        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        String line = br.readLine();

        logger.info("line : {}", line);

        JSONObject jsonCertification = new JSONObject(line);
        JSONObject jsonCertification2 = jsonCertification.getJSONObject("checkMileageCertification");

        final CheckMileageCertification parameterCertification = new CheckMileageCertification();

        // 이미 요청한 인증정보를 확인하기 위해서 미리 세팅한다.
        parameterCertification.setPhoneNumber(jsonCertification2.getString("phoneNumber"));
        parameterCertification.setActivateYn(jsonCertification2.getString("activateYn"));
        parameterCertification.setModifyDate(jsonCertification2.getString("modifyDate"));
        parameterCertification.setRegisterDate(jsonCertification2.getString("registerDate"));

        /*
        사용자가 인증번호 요청버튼을 연속으로 누를 수도 있기 때문에
        기존에 요청한 정보가 있는지 확인하고, 있으면 비활성화 시킨 후 다시 인증번호를 송신 한다.
         */
        try {
            if(("1").equals(this.checkMileageCertificationService.selectExistCertificationInformation(parameterCertification).getTotalCount())) {
                logger.info("already exist user's request certification. so deactivate before request.");

                CheckMileageCertification resultCertification = this.checkMileageCertificationService.selectCertificationInformation(parameterCertification);

                String idCheckMileageCertifications = resultCertification.getIdCheckMileageCertifications();

                // 기존 인증번호를 비활성화 한다.
                CheckMileageCertification parameter = new CheckMileageCertification();
                parameter.setIdCheckMileageCertifications(idCheckMileageCertifications);
                parameter.setActivateYn("N");
                parameter.setModifyDate(this.checkMileageCommon.getCurrentDate());

                logger.info("idCheckMileageCertifications : {}", parameter.getIdCheckMileageCertifications());
                logger.info("activateYn                   : {}", parameter.getActivateYn());
                logger.info("modifyDate                   : {}", parameter.getModifyDate());

                this.checkMileageCertificationService.updateDeactivateCertification(parameter);
            }
        } catch (Exception e) {
            logger.info("error occurred while deactivate user before request.");
        }

        REQUEST_ADMISSION = true;

        // 인증번호를 생성한다.
        String certificationNumber = String.valueOf(checkMileageCommon.getCertificationNumber());

        logger.info("certificationNumber : {}", certificationNumber);

        // 가져온 인증번호를 세팅 한다.
        parameterCertification.setCertificationNumber(certificationNumber);

        logger.info("phoneNumber         : {}", parameterCertification.getPhoneNumber());
        logger.info("certificationNumber : {}", parameterCertification.getCertificationNumber());
        logger.info("activateYn          : {}", parameterCertification.getActivateYn());
        logger.info("modifyDate          : {}", parameterCertification.getModifyDate());
        logger.info("registerDate        : {}", parameterCertification.getRegisterDate());

        CheckMileageCertification result = new CheckMileageCertification();

        try {
            this.checkMileageCertificationService.registerCertification(parameterCertification);

            // 인증번호를 SMS 로 전송한다.
            CheckMileageCertification parameterSMS = new CheckMileageCertification();
            parameterSMS.setMsg("[캐럿마일리지]" + " 사용자 인증번호 [" + certificationNumber + "]를 입력하세요.");
            parameterSMS.setRphone(checkMileageCommon.getSMSPhoneNumber(parameterCertification.getPhoneNumber())); // 수신자 전화번호.
            parameterSMS.setSphone1("02");
            parameterSMS.setSphone2("3775");
            parameterSMS.setSphone3("3799");
            parameterSMS.setRdate("");
            parameterSMS.setRtime("");
            parameterSMS.setTestflag("");
            parameterSMS.setDestination(checkMileageCommon.getSMSPhoneNumber(parameterCertification.getPhoneNumber()));
            parameterSMS.setRepeatFlag("");
            parameterSMS.setRepeatNum("");
            parameterSMS.setRepeatTime("");
            parameterSMS.setNointeractive("");

            logger.info("sms : {}", checkMileageCommon.getSMSPhoneNumber(parameterCertification.getPhoneNumber()));

            checkMileageCommon.sendSMS(parameterSMS);

            result.setResult("SUCCESS");
        } catch (Exception e) {
            result.setResult("FAIL");
        }

        /*
        thread 가 돌면서 5분안에 requestAdmission 메소드가 호출되는지 확인한다.
        만약 호출되지 않으면 기존 인증번호를 비활성화 하고 새로운 인증번호를 요청하라는 SMS 를 전송 한다.
         */
        taskExecutor.execute(new Runnable() {
            @Override
            public void run() {
                logger.info("taskExecutor start : {}", parameterCertification.getPhoneNumber());

                // 1초에 한번씩 REQUEST_ADMISSION 이 false 로 업데이트 되었는지 확인 한다.
                while (true) {
                    try {
                        Thread.sleep(1000);
                        REQUEST_INTERVAL++;
                    } catch (Exception e) {
                        logger.error("error occurred on taskExecutor.");
                    }

                    // Thread 는 2분간 작동하면서 requestAdmission 메소드가 호출되었는지 확인 한다.
                    if(REQUEST_INTERVAL != 120) {
                        // 만약 requestAdmission 메소드가 호출되었으면 thread 를 종료 한다.
                        if(!REQUEST_ADMISSION) {
                            logger.info("taskExecutor stop.");

                            // taskExecutor 실행 횟수 확인 초기화.
                            REQUEST_INTERVAL = 0;

                            break;
                        }
                    } else {
                        // 만약 5분내에 requestAdmission 메소드가 호출되지 않으면 인증 요청을 취소하고 사용자에게 SMS 를 전송 한다.
                        if(REQUEST_ADMISSION) {
                            REQUEST_ADMISSION = false;

                            // 기존 인증번호를 비활성화 한다.
                            // 저장된 사용자의 인증 정보를 가져 온다.
                            CheckMileageCertification resultCertification = checkMileageCertificationService.selectCertificationInformation(parameterCertification);

                            String idCheckMileageCertifications = resultCertification.getIdCheckMileageCertifications();

                            CheckMileageCertification parameter = new CheckMileageCertification();
                            parameter.setIdCheckMileageCertifications(idCheckMileageCertifications);
                            parameter.setActivateYn("N");
                            parameter.setModifyDate(checkMileageCommon.getCurrentDate());

                            logger.info("idCheckMileageCertifications : {}", parameter.getIdCheckMileageCertifications());
                            logger.info("activateYn                   : {}", parameter.getActivateYn());
                            logger.info("modifyDate                   : {}", parameter.getModifyDate());

                            checkMileageCertificationService.updateDeactivateCertification(parameter);

                            /*
                            호스팅 업체에서 제공받은 SMS 전송 로직 삽입해야 함.
                            "시간초과. 인증번호를 다시 요청하세요."
                             */
                            // 인증번호를 SMS 로 전송한다.
                            CheckMileageCertification parameterSMS = new CheckMileageCertification();
                            parameterSMS.setMsg("[캐럿마일리지]" + " 시간초과. 인증번호를 다시 요청하세요.");
                            parameterSMS.setRphone(checkMileageCommon.getSMSPhoneNumber(parameterCertification.getPhoneNumber())); // 수신자 전화번호.
                            parameterSMS.setSphone1("02");
                            parameterSMS.setSphone2("3775");
                            parameterSMS.setSphone3("3799");
                            parameterSMS.setRdate("");
                            parameterSMS.setRtime("");
                            parameterSMS.setTestflag("");
                            parameterSMS.setDestination(checkMileageCommon.getSMSPhoneNumber(parameterCertification.getPhoneNumber()));
                            parameterSMS.setRepeatFlag("");
                            parameterSMS.setRepeatNum("");
                            parameterSMS.setRepeatTime("");
                            parameterSMS.setNointeractive("");

                            logger.info("sms : {}", checkMileageCommon.getSMSPhoneNumber(parameterCertification.getPhoneNumber()));

                            try {
                                checkMileageCommon.sendSMS(parameterSMS);
                            } catch (Exception e) {
                                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
                            }

                            // taskExecutor 실행 횟수 확인 초기화.
                            REQUEST_INTERVAL = 0;

                            break;
                        }
                    }
                }
            }
        });

        return result;
    }

    /**
     * 해당 사용자가 SMS 로 받은 인증번호를 보내서 인증 허가를 요청 한다.
     *
     * 1. database 에 저장되어 있는 인증 정보와 비교 한다.
     * 2. 결과를 반환해서 인증 프로세스를 마친다.
     *
     * @param is
     * @return
     * @throws Exception
     */
    @POST
    @Path("/requestAdmission")
    @Consumes({"application/json", "application/xml"})
    @Produces({"application/json", "application/xml"})
    public CheckMileageCertification requestAdmission(InputStream is) throws Exception {
        logger.info("requestAdmission");

        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        String line = br.readLine();

        logger.info("line : {}", line);

        JSONObject jsonCertification = new JSONObject(line);
        JSONObject jsonCertification2 = jsonCertification.getJSONObject("checkMileageCertification");

        CheckMileageCertification parameterCertification = new CheckMileageCertification();

        parameterCertification.setPhoneNumber(jsonCertification2.getString("phoneNumber"));
        parameterCertification.setCertificationNumber(jsonCertification2.getString("certificationNumber"));
        parameterCertification.setActivateYn(jsonCertification2.getString("activateYn"));

        logger.info("phoneNumber         : {}", parameterCertification.getPhoneNumber());
        logger.info("certificationNumber : {}", parameterCertification.getCertificationNumber());
        logger.info("activateYn          : {}", parameterCertification.getActivateYn());

        CheckMileageCertification result = new CheckMileageCertification();

        try {
            REQUEST_ADMISSION = false;

            // 저장된 사용자의 인증 정보가 있는지 확인 한다.
            if(("1").equals(this.checkMileageCertificationService.selectExistCertificationInformation(parameterCertification).getTotalCount())) {
                // 저장된 사용자의 인증 정보를 가져 온다.
                CheckMileageCertification resultCertification = this.checkMileageCertificationService.selectCertificationInformation(parameterCertification);

                String idCheckMileageCertifications = resultCertification.getIdCheckMileageCertifications();
                String certificationNumber = resultCertification.getCertificationNumber();

                if((certificationNumber).equals(parameterCertification.getCertificationNumber())) {
                    // 로직 성공. 인증 번호 일치.
                    result.setResult("SUCCESS_ADMISSION");

                    // 기존 인증번호를 비활성화 한다.
                    CheckMileageCertification parameter = new CheckMileageCertification();
                    parameter.setIdCheckMileageCertifications(idCheckMileageCertifications);
                    parameter.setActivateYn("N");
                    parameter.setModifyDate(this.checkMileageCommon.getCurrentDate());

                    logger.info("idCheckMileageCertifications : {}", parameter.getIdCheckMileageCertifications());
                    logger.info("activateYn                   : {}", parameter.getActivateYn());
                    logger.info("modifyDate                   : {}", parameter.getModifyDate());

                    this.checkMileageCertificationService.updateDeactivateCertification(parameter);
                } else {
                    // 로직 성공. 인증 번호 불일치.
                    result.setResult("SUCCESS_NOT_ADMISSION");

                    // 기존 인증번호를 비활성화 한다.
                    CheckMileageCertification parameter = new CheckMileageCertification();
                    parameter.setIdCheckMileageCertifications(idCheckMileageCertifications);
                    parameter.setActivateYn("N");
                    parameter.setModifyDate(this.checkMileageCommon.getCurrentDate());

                    logger.info("idCheckMileageCertifications : {}", parameter.getIdCheckMileageCertifications());
                    logger.info("activateYn                   : {}", parameter.getActivateYn());
                    logger.info("modifyDate                   : {}", parameter.getModifyDate());

                    this.checkMileageCertificationService.updateDeactivateCertification(parameter);
                }
            } else {
                // 저장된 사용자의 인증 정보가 없다.
                result.setResult("SUCCESS_NON_EXIST");

                CheckMileageCertification parameterSMS = new CheckMileageCertification();
                parameterSMS.setMsg("일치하는 인증정보가 없습니다." + "\n" + "다시 시도하세요.");
                parameterSMS.setRphone(checkMileageCommon.getSMSPhoneNumber(parameterCertification.getPhoneNumber())); // 수신자 전화번호.
                parameterSMS.setSphone1("02");
                parameterSMS.setSphone2("3775");
                parameterSMS.setSphone3("3799");
                parameterSMS.setRdate("");
                parameterSMS.setRtime("");
                parameterSMS.setTestflag("");
                parameterSMS.setDestination(checkMileageCommon.getSMSPhoneNumber(parameterCertification.getPhoneNumber()));
                parameterSMS.setRepeatFlag("");
                parameterSMS.setRepeatNum("");
                parameterSMS.setRepeatTime("");
                parameterSMS.setNointeractive("");

                logger.info("sms : {}", checkMileageCommon.getSMSPhoneNumber(parameterCertification.getPhoneNumber()));

                try {
                    checkMileageCommon.sendSMS(parameterSMS);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            logger.error("FAIL");

            // 로직 실패.
            result.setResult("FAIL");
        }

        return result;
    }
}
